package com.king.designpatterns.创造型模式.单例模式;

/**
 * 单例模式（Singleton Pattern）是最简单的一种设计模式。
 * 定义：确保一个类只有一个实例，而且自行实例化并向整个系统提供这个实例。
 *
 * 表现：
 *      ■ 饿汉式单例类：类加载时，就进行对象实例化；
 *      ■ 懒汉式单例类：第一次引用类时，才进行对象实例化。
 *      ■ 有状态单例类：一个有状态单例类的对象一般是可变的，通常当做状态库使用。例如，给系统提供一个唯一的序列号。
 *      ■ 无状态单例类：无状态单例类是不变的，通常用来提供工具性的功能方法。例如， IO或数据库访问等。
 *
 * 区别：
 *      ■ 饿汉式单例类在被加载时实例化，而懒汉式单例类在第一次引用时实例化；
 *      ■ 从资源利用效率上说，饿汉式单例类要差一些，但从速度和反应时间角度来讲，饿汉式单例类则比懒汉式单例类稍好些；
 *
 * 优点：
 *      ■ 由于单例模式在内存中只有一个实例，减少了内存的开支，特别是一个对象需要频繁地创建、销毁，而且创建或销毁的性能又无法优化时，
 *          单例模式的优势就非常明显。
 *      ■ 由于单例模式只生成一个实例，所以减少了系统的性能开销，当一个对象的产生需要比较多资源时，如读取配置、产生其他依赖对象时，
 *          则可以通过在启用时直接产生一个单例对象，然后用永久驻留内存的方式来解决。
 *      ■ 单例模式可以避免对资源的多重占用，例如，一个写文件动作，由于只有一个实例存在于内存中，避免了对同一个资源文件的同时操作。
 *      ■ 单例模式可以在系统设置全局的访问点，优化和共享资源访问，例如，可以设计一个单例类，负责所有数据表的映射处理。
 *
 * 缺点：
 *      ■ 单例模式无法创建子类，扩展困难，若要扩展，除了修改代码基本上没有第二种途径可以实现。
 *      ■ 单例模式对测试不利。在并行开发环境中，如果采用单例模式的类没有完成，是不能进行测试的；单例模式的类通常不会实现接口，
 *          这也妨碍了使用mock的方式虚拟一个对象。
 *      ■ 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑，而不关心它是否是单例的，是不是要用单例模式取决于环境，
 *          单例模式把“要单例”和业务逻辑融合在一起。
 *
 * 场景：
 *      ■ 要求生成唯一序列号的环境。
 *      ■ 在整个项目中需要一个共享访问点或共享数据，例如，一个Web页面上的计数器，可以不用把每次刷新都记录到数据库中，使用单例模式保持计数器的值，并确保是线程安全的。
 *      ■ 创建一个对象需要消耗的资源过多，如访问IO和数据库等资源。
 *      ■ 需要定义大量的静态常量和静态方法（如工具类）的环境，可以采用单例模式（也可以采用直接声明为static的方式）。
 *
 * @author king
 * @date 2021-06-04 23:56
 */
public class SingletonPattern {
    private static SingletonPattern sp = new SingletonPattern();
    private int num = 0;

    public static SingletonPattern getInstance(){
        return sp;
    }

    // 使用单例模式记录访问次数
    public synchronized int getNum(){
        return ++num;
    }

    // 测试单例模式
    public static void main(String[] args) {
        NumThread a = new NumThread("A");
        NumThread b = new NumThread("B");

        a.start();
        b.start();
    }

    static class NumThread extends Thread {
        private String threadName;

        public NumThread(String name){
            this.threadName = name;
        }
        @Override
        public void run() {
            SingletonPattern sp = SingletonPattern.getInstance();
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + "第" + sp.getNum() + "次访问！");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
